/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.handler.codec;

import static io.netty.util.internal.ObjectUtil.checkPositive;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;

import java.util.List;

/**
 * 定长解码器
 * @author 二十
 */
public class FixedLengthFrameDecoder extends ByteToMessageDecoder {
    //规定的数据帧的长度，相当于一种规则
    private final int frameLength;

    //假设frameLength =  10 ， 那么每次从堆积区取数据的时候都是直接长度为 10  进行一次 截断拿出来。
    public FixedLengthFrameDecoder(int frameLength) {
        checkPositive(frameLength, "frameLength");
        this.frameLength = frameLength;
    }

    /**
     * 核心方法     解码
     * @param ctx  包装当前decoder的context
     * @param in   堆积区
     * @param out  装有 socket 底层数据
     * @throws Exception
     */
    @Override
    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        Object decoded = decode(ctx, in);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    /**
     * Create a frame out of the {@link ByteBuf} and return it.
     *
     * @param   ctx             the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
     * @param   in              the {@link ByteBuf} from which to read data
     * @return  frame           the {@link ByteBuf} which represent the frame or {@code null} if no frame could
     *                          be created.
     */
    protected Object decode(
            @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        //检查堆积区的可读数据量 ， 条件成立，说明现在剩下的数据量不足够一次解码，可能是半包这种 ， 所以直接返回null。
        if (in.readableBytes() < frameLength) {
            return null;
        } else {
            //从原堆积区读取指定长度的字节量创建出来新的ByteBuf，并且会增加堆积区的引用计数。
            //为什么要增加引用计数值呢？
            //新创建出来的ByteBuf是原堆积区的一个子集，也就是占用的内存是原堆积区内存的一部分，在切片未释放内存之前，原堆积区不能释放内存，否则切片就找不到自己的内存了。
            //切片ByteBuf.release 会将堆积区的ByteBuf的引用计数 -1 。
            //看一下具体的实现之一：
            return in.readRetainedSlice(frameLength);
        }
    }
}
